package com.quwan.manager.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.common.base.Splitter;
import com.quwan.common.BaseService;
import com.quwan.common.Check;
import com.quwan.common.enums.*;
import com.quwan.common.exception.ExpCode;
import com.quwan.common.exception.ResponseException;
import com.quwan.common.exception.ServiceException;
import com.quwan.common.result.PageResult;
import com.quwan.component.AsyncTaskComponent;
import com.quwan.config.yml.OssConfig;
import com.quwan.constans.AppConst;
import com.quwan.entity.*;
import com.quwan.manager.controller.dto.DispatchApplyDto;
import com.quwan.manager.controller.dto.DispatchDto;
import com.quwan.manager.controller.dto.PublishOrderListDTO;
import com.quwan.manager.service.ManagerOrderService;
import com.quwan.mapper.*;
import com.quwan.utils.Parse;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author quan
 * @date 2021-10-29 20:49
 */

@Service
@RequiredArgsConstructor
public class ManagerOrderServiceImpl extends BaseService implements ManagerOrderService {
    private final C2cOrderMapper orderMapper;
    private final C2cOrderItemMapper orderItemMapper;
    private final C2cOrderCancelMapper orderCancelMapper;
    private final TMemberMapper memberMapper;
    private final TPublishOrderMapper publishOrderMapper;
    private final TMemberBalanceLogMapper memberBalanceLogMapper;
    private final TOrderSplitMapper orderSplitMapper;
    private final AsyncTaskComponent asyncTaskComponent;
    private final OssConfig ossConfig;
    private final TDispatchOrderMapper dispatchOrderMapper;
    private final TDispatchOrderApplyMapper applyMapper;
    private final TMemberBalanceLogMapper balanceLogMapper;
    private final TScaleSettingMapper scaleSettingMapper;


    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    @Override
    public boolean cancelOrder(String orderId, String type) {
        if ("0".equalsIgnoreCase(type)) {
            //todo 不同意处理方案
            return true;
        }

        //DONE:同意处理方案
        C2cOrder order =
                orderMapper
                        .selectOne(
                                new LambdaQueryWrapper<C2cOrder>()
                                        .select(C2cOrder::getPayStatus, C2cOrder::getOrderStatus, C2cOrder::getPayPrice,
                                                C2cOrder::getMId, C2cOrder::getProductPrice, C2cOrder::getPublishOrderId)
                                        .eq(C2cOrder::getId, orderId)
                        );
        if (order == null || !AppConst.ORDER_CANCEL.equals(order.getOrderStatus()) || !PayStatusEnum.FINISH.getType().equals(order.getPayStatus()))
            throw new ResponseException(ExpCode.ORDER_INFO_ERR.getCode(), ExpCode.ORDER_INFO_ERR.getMsg());
        Long mId = order.getMId();

        TMember member =
                memberMapper
                        .selectOne(
                                new LambdaQueryWrapper<TMember>()
                                        .select(TMember::getBalance, TMember::getVersion)
                                        .eq(TMember::getId, mId)
                        );
        if (member == null)
            throw new ResponseException(ExpCode.MEMBER_NOT_EXIST.getCode(), ExpCode.MEMBER_NOT_EXIST.getMsg());
        BigDecimal balance = member.getBalance();
        Long version = member.getVersion();
        //BigDecimal companyGrain = order.getPayPrice().multiply(WebConst.cancel_order_fee);
        BigDecimal cancelPrice = order.getPayPrice();//.subtract(companyGrain);

        int memberRow =
                memberMapper
                        .update(null,
                                new LambdaUpdateWrapper<TMember>()
                                        .set(TMember::getBalance, balance.add(cancelPrice).toString())
                                        .set(TMember::getVersion, version + 1)
                                        .eq(TMember::getId, mId)
                                        .eq(TMember::getVersion, version)
                        );
        int logRow =
                memberBalanceLogMapper
                        .insert(
                                new TMemberBalanceLog()
                                        .setTradeNo(orderId)
                                        .setTradeType(BalanceLogEnum.CANCEL_ORDER.getType())
                                        .setBeforeM(balance)
                                        .setMoney(cancelPrice)
                                        .setBalance(balance.add(cancelPrice))
                                        .setMethod(Integer.parseInt(PaymentEnum.SYSTEM_PAY.getType()))
                                        .setTips(BalanceLogEnum.CANCEL_ORDER.getTips())
                                        .setPayTime(LocalDateTime.now())
                                        .setMId(mId)
                                        .setStatus(1)
                        );
        int splitRow =
                orderSplitMapper
                        .insert(
                                new TOrderSplit()
                                        .setC2cOrderId(Long.parseLong(orderId))
                                        .setRealPrice(order.getPayPrice())
                                        .setOriginOrderPrice(order.getProductPrice())
                                        .setMemberCommission(cancelPrice)
                                        // .setCompanyCommission(companyGrain)
                                        .setStatus(0)

                        );
        int delOrderRow =
                orderMapper
                        .delete(
                                new QueryWrapper<>(
                                        new C2cOrder().setId(orderId)
                                )
                        );
        int delOrderItemRow =
                orderItemMapper
                        .delete(
                                new QueryWrapper<>(
                                        new C2cOrderItem().setC2cOrderId(orderId)
                                )

                        );
        orderCancelMapper
                .update(null,
                        new LambdaUpdateWrapper<C2cOrderCancel>()
                                .set(C2cOrderCancel::getHandleTime, LocalDateTime.now())
                                .eq(C2cOrderCancel::getId, orderId)
                );
        asyncTaskComponent.updPublishCancelCount(order.getPublishOrderId());

        if (memberRow == 1 && logRow == 1 && delOrderItemRow == 1 && delOrderRow == 1 && splitRow == 1)
            return true;
        else
            throw new ServiceException(ExpCode.DB_UPDATE_ERR.getMsg());
    }

    @Override
    public PageResult<PublishOrderListDTO> getPublishOrderList(int pageNo,
                                                               int pageSize,
                                                               String publishOrderMember,
                                                               String categoryId,
                                                               String status,
                                                               String order,
                                                               String audit,
                                                               String mId,
                                                               String content,
                                                               String id) {
        //ascend undefined
        Integer orderBy = "undefined".equalsIgnoreCase(order) ? 0 : "ascend".equalsIgnoreCase(order) ? 1 : "descend".equalsIgnoreCase(order) ? 2 : 0;
        Page<Map<String, String>> res =
                publishOrderMapper
                        .managerSelectMemberPublishOrderList(new Page<>(pageNo, pageSize), publishOrderMember, categoryId, status, orderBy, audit, mId, content,id);

        if (res.getRecords() == null)
            return new PageResult<>();
        return new PageResult<PublishOrderListDTO>()
                .setPages(res.getPages())
                .setSize(res.getSize())
                .setCurrent(res.getCurrent())
                .setTotal(res.getTotal())
                .setRecords(res
                        .getRecords()
                        .stream()
                        .map(m ->
                                new PublishOrderListDTO()
                                        .setOrderId(String.valueOf(m.get("id")))
                                        .setMId(String.valueOf(m.get("m_id")))
                                        .setCategory(String.valueOf(m.get("caption")))
                                        .setDate(String.valueOf(m.get("add_time")).length() > 16 ? String.valueOf(m.get("add_time")).substring(0, 16) : String.valueOf(m.get("add_time")))
                                        .setNickName(String.valueOf(m.get("nick_name")))
                                        .setPrice(String.valueOf(m.get("price")))
                                        .setOtherDesc(String.valueOf(m.get("other_desc")))
                                        .setPid(String.valueOf(m.get("p_id")))
                                        .setStatus(Integer.valueOf(String.valueOf(m.get("status"))))
                                        .setOrderNum(Integer.valueOf(String.valueOf(m.get("order_num"))))
                                        .setAudit(String.valueOf(m.get("audit")))
                                        .setImgs(Splitter.on(",").splitToList(String.valueOf(m.get("imgs"))).stream().map(url -> ossConfig.getHostname() + "/" + url).collect(Collectors.toList()))
                        )
                        .collect(Collectors.toList())
                );

    }

    /**
     * 发单——关闭
     *
     * @param orderId
     * @return
     */
    public boolean orderManagerPublishCancel(String orderId, Integer status) {
        return publishOrderMapper
                .update(null,
                        new LambdaUpdateWrapper<TPublishOrder>()
                                .set(TPublishOrder::getStatus, status)
                                .set(TPublishOrder::getAudit, CommonStatus.NORMAL.getType())
                                .eq(TPublishOrder::getId, orderId)
                ) == 1;
    }

    @Override
    public boolean orderPublishChangeOrderNum(String orderId, Integer orderNum) {
        return publishOrderMapper.update(null,
                new LambdaUpdateWrapper<TPublishOrder>()
                        .set(TPublishOrder::getOrderNum, orderNum)
                        .eq(TPublishOrder::getId, orderId)
        ) == 1;
    }

    @Override
    public boolean orderPass(String orderId) {
        return publishOrderMapper
                .update(null,
                        new LambdaUpdateWrapper<TPublishOrder>()
                                .set(TPublishOrder::getAudit, CommonStatus.NORMAL.getType())
                                .eq(TPublishOrder::getId, orderId)) == 1;
    }

    @Override
    public PageResult<DispatchDto> dispatchOrderList(int page, int size, String orderId, String dispatchMember, String serviceMember, String categoryId, String content, String status, String sort, Integer audit) {
        Integer orderBy = "undefined".equalsIgnoreCase(sort) || StringUtils.isBlank(sort) ? 0 :
                "ascend".equalsIgnoreCase(sort) ? 1 :
                        "descend".equalsIgnoreCase(sort) ? 2 :
                                0;

        Page<DispatchDto> res = dispatchOrderMapper.selectDispatchOrderList(new Page<>(page, size), orderId, dispatchMember, serviceMember, categoryId, content, status, orderBy, audit.toString());
        if (res.getTotal() == 0)
            return new PageResult<>();
        return new PageResult<DispatchDto>()
                .setPages(res.getPages())
                .setTotal(res.getTotal())
                .setCurrent(res.getCurrent())
                .setSize(res.getSize())
                .setRecords(
                        res
                                .getRecords()
                                .stream()
                                .peek(order ->
                                        order
                                                .setImages(
                                                        strToList(",", order.getImgs())
                                                                .stream()
                                                                .filter(StringUtils::isNotBlank)
                                                                .map(url -> ossConfig.getHostname() + "/" + url)
                                                                .collect(Collectors.toList()))
                                ).collect(Collectors.toList())

                );
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public boolean dispatchChangeStatus(String id, Integer status) {
        TDispatchOrder order = dispatchOrderMapper.selectById(id);
        Check.notNull(order, ExpCode.ORDER_NOT_EXIST);
        Check.state(order.getStatus() < DispatchOrderStatusEnum.processing.getStatus() && order.getBeforeStatus().equals(PayStatusEnum.FINISH.getType()), ExpCode.ORDER_STATUS_UN_UPD);

        if (status == -2) {
            //审核不通过
            //派单人取消订单  金额退回余额
            TMember member = memberMapper.selectOne(new QueryWrapper<>(new TMember().setId(order.getMId())));
            Check.notNull(member, ExpCode.MEMBER_NOT_EXIST);
            BigDecimal price = order.getBeforePrice();
            BigDecimal balance = member.getBalance();

            int memberRow = memberMapper.update(null,
                    new LambdaUpdateWrapper<TMember>()
                            .set(TMember::getBalance, balance.add(price))
                            .set(TMember::getVersion, member.getVersion() + 1)
                            .eq(TMember::getAutoId, member.getAutoId())
                            .eq(TMember::getVersion, member.getVersion())

            );

            applyMapper.update(
                    null,
                    new LambdaUpdateWrapper<TDispatchOrderApply>()
                            .set(TDispatchOrderApply::getStatus, DispatchOrderStatusEnum.del.getStatus())
                            .eq(TDispatchOrderApply::getOrderId, order.getId()));
            int logRow = balanceLogMapper.insert(
                    new TMemberBalanceLog()
                            .setMId(member.getId())
                            .setTips(BalanceLogEnum.DISPATCH_CANCEL.getTips())
                            .setTradeType(BalanceLogEnum.DISPATCH_CANCEL.getType())
                            .setStatus(PayStatusEnum.FINISH.getType())
                            .setPrePayId(order.getPrePayId())
                            .setBalance(balance.add(price))
                            .setMoney(price)
                            .setBeforeM(balance)
                            .setMethod(order.getBeforePayMethod())
                            .setTradeNo(order.getOrderId().toString())
                            .setPayTime(LocalDateTime.now())
                            .setTransactionId(order.getBeforeTransactionId() + "")
            );
            int orderRow = dispatchOrderMapper.updateById(new TDispatchOrder().setId(Parse.toLong(id)).setStatus(-2).setAudit(1));
            Check.state(memberRow == 1 && orderRow == 1 && logRow == 1, ExpCode.DB_UPDATE_ERR);
            return true;
        } else {
            return dispatchOrderMapper.updateById(new TDispatchOrder().setId(Parse.toLong(id)).setAudit(1)) > 0;

        }


    }

    @Override
    public boolean updDispatchOrderOrderNum(String id, Integer orderNum) {
        return dispatchOrderMapper.updateById(new TDispatchOrder().setId(Parse.toLong(id)).setOrderNum(orderNum)) == 1;
    }

    @Override
    public PageResult<DispatchApplyDto> dispatchApplyList(int page, int size, String id) {
        Page<DispatchApplyDto> res = applyMapper.selectApplyListPage(new Page<>(page, size), id);
        return new PageResult<DispatchApplyDto>()
                .setSize(res.getSize())
                .setCurrent(res.getCurrent())
                .setTotal(res.getTotal())
                .setPages(res.getPages())
                .setRecords(
                        res.getRecords()
                                .stream()
                                .map(m -> m.setImg(Optional.ofNullable(m.getImg()).map(url -> ossConfig.getHostname() + "/" + url).orElse("")))
                                .collect(Collectors.toList())
                );
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW,rollbackFor = Exception.class)
    @Override
    public void cancelDispatchOrder(String id, Integer type) {
        TDispatchOrder dispatchOrder = dispatchOrderMapper.selectById(id);
        Check.notNull(dispatchOrder, ExpCode.ORDER_NOT_EXIST);

        BigDecimal cancelPrice = BigDecimal.ZERO;
        BigDecimal cancelScale = BigDecimal.ZERO;
        BigDecimal cancelFee = BigDecimal.ZERO;

        Integer beforeStatus = dispatchOrder.getBeforeStatus();
        Integer payStatus = dispatchOrder.getPayStatus();

        if (DispatchOrderStatusEnum.cancel.getStatus().equals(dispatchOrder.getStatus()) ||
                DispatchOrderStatusEnum.del.getStatus().equals(dispatchOrder.getStatus()) ||
                DispatchOrderStatusEnum.finish.getStatus().equals(dispatchOrder.getStatus()))
            throw new ResponseException(ExpCode.ORDER_STATUS_NOT_SPORT_CANCEL.getCode(), ExpCode.ORDER_STATUS_NOT_SPORT_CANCEL.getMsg());
        if (PayStatusEnum.FINISH.getType().equals(payStatus) && PayStatusEnum.FINISH.getType().equals(beforeStatus))
            cancelPrice = dispatchOrder.getPrice();
        else if (PayStatusEnum.FINISH.getType().equals(beforeStatus) && !PayStatusEnum.FINISH.getType().equals(payStatus))
            cancelPrice = dispatchOrder.getBeforePrice();
        else
            throw new ResponseException(ExpCode.ORDER_INFO_ERR.getCode(), ExpCode.ORDER_INFO_ERR.getMsg());

        if (cancelPrice.compareTo(BigDecimal.ZERO) == 0)
            throw new ResponseException(500, "派单取消金额错误");

        if (type == 1) {
            TScaleSetting tScaleSetting = scaleSettingMapper.selectOne(new QueryWrapper<>(new TScaleSetting().setType(OrderScaleEnum.DISPATCH_ORDER_CANCEL.getType())));
            cancelScale = Optional.ofNullable(tScaleSetting).map(TScaleSetting::getScale).orElse(new BigDecimal(OrderScaleEnum.DISPATCH_ORDER_CANCEL.getDefScale()));
            cancelFee = cancelPrice.multiply(cancelScale).setScale(2, 5);
            cancelPrice  = cancelPrice.subtract(cancelFee);
        }
        //派单人取消订单  金额退回余额
        TMember member = memberMapper.selectOne(new QueryWrapper<>(new TMember().setId(dispatchOrder.getMId())));
        Check.notNull(member, ExpCode.MEMBER_NOT_EXIST);
        BigDecimal balance = member.getBalance();
        int memberRow = memberMapper.update(null,
                new LambdaUpdateWrapper<TMember>()
                        .set(TMember::getBalance, balance.add(cancelPrice))
                        .set(TMember::getVersion, member.getVersion() + 1)
                        .eq(TMember::getAutoId, member.getAutoId())
                        .eq(TMember::getVersion, member.getVersion())

        );

        int orderRow = dispatchOrderMapper.update(null,
                new LambdaUpdateWrapper<TDispatchOrder>()
                        .set(TDispatchOrder::getStatus, DispatchOrderStatusEnum.cancel.getStatus())
                        .set(TDispatchOrder::getAudit,1)
                        .eq(TDispatchOrder::getId, dispatchOrder.getId())
        );
        applyMapper.update(null,
                new LambdaUpdateWrapper<TDispatchOrderApply>()
                        .set(TDispatchOrderApply::getStatus, DispatchOrderStatusEnum.del.getStatus())
                        .eq(TDispatchOrderApply::getOrderId, dispatchOrder.getId()));
        int logRow = balanceLogMapper.insert(
                new TMemberBalanceLog()
                        .setMId(member.getId())
                        .setTips(BalanceLogEnum.DISPATCH_CANCEL.getTips())
                        .setTradeType(BalanceLogEnum.DISPATCH_CANCEL.getType())
                        .setStatus(PayStatusEnum.FINISH.getType())
                        .setPrePayId(dispatchOrder.getPrePayId())
                        .setBalance(balance.add(cancelPrice))
                        .setMoney(cancelPrice)
                        .setBeforeM(balance)
                        .setFeeScale(cancelScale.stripTrailingZeros().toPlainString())
                        .setFee(cancelFee)
                        .setMethod(dispatchOrder.getPayMethod())
                        .setTradeNo(dispatchOrder.getOrderId().toString())
                        .setPayTime(LocalDateTime.now())
                        .setTransactionId(dispatchOrder.getTransactionId() + "")
        );
        Check.state(memberRow == 1 && orderRow == 1 && logRow == 1, ExpCode.DB_UPDATE_ERR);

    }

    @Override
    public void cancelPublishOrder(String id, Integer type) {
        //要取消的订单信息
        C2cOrder order =
                orderMapper
                        .selectOne(
                                new QueryWrapper<>(
                                        new C2cOrder().setId(id))
                        );

        if (order == null)
            throw new ResponseException(ExpCode.ORDER_NOT_EXIST.getCode(), ExpCode.ORDER_NOT_EXIST.getMsg());

        //取消请求发起人不是该下单人|订单状态不是支付状态 | 未接单，已接单  状态
        if ( OrderStatusEnum.FINISH.getStatus().equals(order.getOrderStatus()) ||!PayStatusEnum.FINISH.getType().equals(order.getPayStatus()))
            throw new ResponseException(ExpCode.ORDER_INFO_ERR.getCode(), ExpCode.ORDER_INFO_ERR.getMsg());


        //会员信息获取
        TMember member = memberMapper
                .selectOne(
                        new LambdaQueryWrapper<TMember>()
                                .select(TMember::getVersion, TMember::getDd, TMember::getBalance)
                                .eq(TMember::getId, order.getMId())
                                .eq(TMember::getStatus, MemberStatusEnum.NORMAL.getCode())
                );
        BigDecimal cancelScale = BigDecimal.ZERO;
        BigDecimal cancelFee = BigDecimal.ZERO;
        Check.notNull(member, ExpCode.LOGIN_INFO_ERR);


        //订单支付价格
        BigDecimal payPrice = order.getPayPrice();
        //优惠价格
        BigDecimal couponPrice = order.getCouponPrice();
        Integer couponStatus = order.getCouponStatus();

        if (type == 1) {
            TScaleSetting tScaleSetting = scaleSettingMapper.selectOne(new QueryWrapper<>(new TScaleSetting().setType(OrderScaleEnum.PUBLISH_ORDER_CANCEL.getType())));
            cancelScale = Optional.ofNullable(tScaleSetting).map(TScaleSetting::getScale).orElse(new BigDecimal(OrderScaleEnum.PUBLISH_ORDER_CANCEL.getDefScale()));
            cancelFee = payPrice.multiply(cancelScale).setScale(2, 5);
            payPrice  = payPrice.subtract(cancelFee);

        }



        //会员余额
        BigDecimal balance = member.getBalance();

        //数据版本
        Long mver = member.getVersion();
        BigDecimal newBalance = balance.add(payPrice);



        //修改会员余额 优惠
        int row = memberMapper.update(
                null,
                new LambdaUpdateWrapper<TMember>()
                        .set(TMember::getBalance, newBalance)
                        .set(TMember::getVersion, mver + 1)
                        .eq(TMember::getId, order.getMId())
                        .eq(TMember::getVersion, mver)
        );
        if (row == 1) {
            //备份取消订单信息
            C2cOrderCancel cancel = new C2cOrderCancel();
            BeanUtils.copyProperties(order, cancel);
            BeanUtils.copyProperties(order, cancel);
            cancel.setCancelReason("后台取消")
                    .setHandleTime(LocalDateTime.now())
                    .setCancelTime(LocalDateTime.now());
            int cancelRow = orderCancelMapper.insert(cancel);
            //删除订单
            int delRow = orderMapper.delete(new QueryWrapper<>(new C2cOrder().setId(id)));
            int delItemRow = orderItemMapper.delete(new QueryWrapper<>(new C2cOrderItem().setC2cOrderId(id)));
            //余额日志添加
            int logRow = memberBalanceLogMapper
                    .insert(
                            new TMemberBalanceLog()
                                    .setTradeNo(id)
                                    .setTradeType(BalanceLogEnum.CANCEL_ORDER.getType())
                                    .setBeforeM(balance)
                                    .setMoney(payPrice)
                                    .setPrePayId(order.getPrePayId())
                                    .setTransactionId(order.getTransactionId())
                                    .setBalance(newBalance)
                                    .setMethod(Integer.parseInt(PaymentEnum.SYSTEM_PAY.getType()))
                                    .setTips(BalanceLogEnum.CANCEL_ORDER.getTips())
                                    .setPayTime(LocalDateTime.now())
                                    .setMId(order.getMId())
                                    .setFee(cancelFee)
                                    .setFeeScale(cancelScale.stripTrailingZeros().toPlainString())
                                    .setStatus(CommonStatus.NORMAL.getType())
                    );

            if (cancelRow != 1 || delRow != 1 || logRow != 1)
                throw new RuntimeException("取消订单 更新数据失败");
        }
    }
}
